#ifndef OBR_SHADER_COMMON_CUH
#define OBR_SHADER_COMMON_CUH

#ifndef __CUDACC__
#define __CUDACC__
#endif

#include <cuda_runtime.h>
#include <cuda_runtime_api.h>
#include <curand_kernel.h>
#include <vector_functions.h>
#include <vector_types.h>

#include <optix_device.h>
#include <optix_types.h>

#include "SharedDS/SharedDS.h"

#include "core/Matrix4x4f.h"
#include "flags.cuh"
#include "flags.h"
#include "helper_math.h"


namespace OBR
{
static __forceinline__ __device__ void *unpackPointer(unsigned int i0, unsigned int i1)
{
    const unsigned long long uptr = static_cast<unsigned long long>(i0) << 32 | i1;
    void *ptr = reinterpret_cast<void *>(uptr);
    return ptr;
}

static __forceinline__ __device__ void packPointer(void *ptr, unsigned int &i0, unsigned int &i1)
{
    const unsigned long long uptr = reinterpret_cast<unsigned long long>(ptr);
    i0 = uptr >> 32;
    i1 = uptr & 0x00000000ffffffff;
}

static __forceinline__ __device__ SamplePRD *getPRD()
{
    const unsigned int u0 = optixGetPayload_0();
    const unsigned int u1 = optixGetPayload_1();
    return reinterpret_cast<SamplePRD *>(unpackPointer(u0, u1));
}

static __forceinline__ __device__ SamplePRD *getPRDWithPayload(uint &u0, uint &u1)
{
    u0 = optixGetPayload_0();
    u1 = optixGetPayload_1();
    return reinterpret_cast<SamplePRD *>(unpackPointer(u0, u1));
}

static __forceinline__ __device__ void setPayloadOcclusion(bool occluded)
{
    optixSetPayload_0(static_cast<unsigned int>(occluded));
}

static __forceinline__ __device__ Spectrum get_fbo(Params *params, uint3 pos, size_t fbo_id)
{
    size_t offset = params->width * params->height * fbo_id + params->width * pos.y + pos.x;
    return params->fbo[offset];
}

static __forceinline__ __device__ void set_fbo(Params *params, uint3 pos, size_t fbo_id,
                                               Spectrum value)
{
    size_t offset = params->width * params->height * fbo_id + params->width * pos.y + pos.x;
    params->fbo[offset] = value;
}

static __forceinline__ __device__ void accumulate_fbo(Params *params, uint3 pos, size_t fbo_id,
                                                      Spectrum value)
{
    size_t offset = params->width * params->height * fbo_id + params->width * pos.y + pos.x;
    params->fbo[offset] += value;
}

static __forceinline__ __device__ void print_float3(float3 val)
{
    printf("(%f, %f, %f)", val.x, val.y, val.z);
}
} // namespace OBR

#endif